#include "MainGameplayScreen.h"
#include "s3e.h"
#include <IwGeomFVec2.h>
#include "Iw2D.h"
#include "SplineHelper.h"
#include "il_assert.h"
#include "IwGx.h"
#include "ScoreSystem.h"
#include <math.h>
#include "AudioSystem.h"
#include "GameScreens.h"

#include "Level1.h"
#include "Level2.h"
#include "Level3.h"
#include "Level4.h"
#include "Level5.h"
#include "Level6.h"
#include "Level7.h"
#include "Level8.h"
#include "Level9.h"
#include "Level10.h"

#include "Tutorial1Level.h"
#include "LevelSingleDock.h"
#include "TwoDockTutorialLevel.h"

#define SCRHEIGHT 320
#define SCRWIDTH 480

#define MAIN_GAMEPLAY_SCREEN_RESOURCE_GROUP_FILE "gameplay/resource.group"
#define MAIN_GAMEPLAY_SCREEN_RESOURCE_GROUP_NAME "gameplay"

#define MAIN_MENU_SCREEN_RESOURCE_GROUP_FILE "menu/resource.group"
#define MAIN_MENU_SCREEN_RESOURCE_GROUP_NAME "menu"

#define LEVEL_COMPLETE_BLINK_SPEED 3.0f

extern AudioSystem* SoundSystem;

MainGameplayScreen::MainGameplayScreen( ScoreSystem* score_system )
	: m_score_system( score_system )
{ }

void MainGameplayScreen::Load()
{
	m_state = GameplayState::NORMAL;        
	m_level_complete_state = 0;

	IwGetResManager()->LoadGroup( MAIN_GAMEPLAY_SCREEN_RESOURCE_GROUP_FILE );
    CIwResGroup *resGroup = IwGetResManager()->GetGroupNamed( MAIN_GAMEPLAY_SCREEN_RESOURCE_GROUP_NAME );
    
    IL_ASSERT( resGroup != NULL, "failed to load the ressource group" );     
    
    // based on the level, load the correct data
    int current_level = m_score_system->GetCurrentLevel();
    
    switch( current_level ) {
		case 0:
			m_current_level = new Tutorial1Level();
		break;
		
		case 1:
			m_current_level = new LevelSingleDock();
		break;
		
		case 2:
			m_current_level = new TwoDockTutorialLevel();
		break;
		
		case 3:
			m_current_level = new Level1();
		break;
		
		case 4:
			m_current_level = new Level2();
		break;
		
		case 5:
			m_current_level = new Level3();
		break;
		
		case 6:
			m_current_level = new Level4();
		break;
		
		case 7:
			m_current_level = new Level5();
		break;
		
		case 8:
			m_current_level = new Level9();
		break;
		
		case 9:
			m_current_level = new Level6();
		break;
		
		case 10:
			m_current_level = new Level7();
		break;
		
		case 11:
			m_current_level = new Level8();
		break;
		
		case 12:
			m_current_level = new Level10();
		break;
    }
    
    m_level_complete.Load( "level_complete" );
    
    // now load the level data
    m_current_level->Load( m_docks, &m_background );
    
    m_water.Load("watur","waturb");

	// 
	m_score_system->StartNew( current_level );

	// load all ships
	for( int i = 0; i < MAX_NUM_SHIPS; ++i ) {
		m_ships[i].Load( m_score_system );
	}
	
	m_gameplay_ui.Load( );
	m_pause_menu.Load();
	m_gameover_menu.Load();
	
	m_active_ship = -1;
}

void MainGameplayScreen::Unload()
{
	for( int i = 0; i < MAX_NUM_DOCKS; ++i ) {
		m_docks[i].Unload( );
	}
	
	// unload all ships again
	for( int i = 0; i < MAX_NUM_SHIPS; ++i ) {
		m_ships[i].Unload();
	}
	
	m_level_complete.Unload();
	m_water.Unload();
	
	m_background.Unload(); 
	
	m_current_level->Unload();
	delete m_current_level;

	m_gameplay_ui.Unload();
	m_pause_menu.Unload();
	m_gameover_menu.Unload();

	IwGetResManager()->DestroyGroup(MAIN_GAMEPLAY_SCREEN_RESOURCE_GROUP_NAME);
}
		
float fsign(float f) {
    if (f > 0) return 1.0f;
    return (f == 0) ? 0.0f : -1.0f;
}
		
// returns the id of the screen it should be switched to
// -1 if the screen shouldn't be switcher
int MainGameplayScreen::Update( float dt )
{
	CIwFVec2 p1;
	CIwFVec2 p2;
	DockType::Enum next_dock_target;
	LevelUpdateReturnStatus::Enum level_update_stat = m_current_level->Update( &next_dock_target, &p1, &p2, dt );

	GameplayState::Enum old_gameplay_state = m_state;
	
	if( level_update_stat == LevelUpdateReturnStatus::LEVEL_COMPLETE ) {
		
		m_state = GameplayState::LEVEL_COMPLETE;
	}
	
	if( m_state == GameplayState::LEVEL_COMPLETE ) {
		m_level_complete_state += LEVEL_COMPLETE_BLINK_SPEED * dt;
		
		if( m_level_complete_state >= (PI/2) ) {
			
			m_score_system->FinishedLevel();
			return GameScreens::MAIN_GAMEPLAY_SCREEN;
		} 
	}
	
	if( level_update_stat == LevelUpdateReturnStatus::SHOW_OVERLAY ) {
		m_state = GameplayState::PAUSED;
		m_active_ship = -1;
	}

	if( m_state == GameplayState::NORMAL ) {
		m_background.Update( dt );
		m_water.Update(dt);
		
		if( m_gameplay_ui.Update( dt ) == true ) {
			// open the menu screen
			m_state = GameplayState::PAUSED;
		}
		
		// check if we should lounch a new ship
		if( level_update_stat == LevelUpdateReturnStatus::START_SHIP ) {	
			// find the first non used ship
			for( int i = 0; i < MAX_NUM_SHIPS; ++i ) {
				if( m_ships[i].IsActive() == false ) {
				
					m_ships[i].Start( next_dock_target, p1, p2 );
					m_current_level->ShipAdded( &m_ships[i] );
					break;
				}
			}
		}

		s3ePointerState touch0state = s3ePointerGetTouchState( 0 );

		if( touch0state & S3E_POINTER_STATE_RELEASED || touch0state & S3E_POINTER_STATE_UP  ) {
			// when the first touch is released we stop all 
			// path editing
			m_active_ship = -1;
		}
		
		if( 
		m_active_ship == -1 &&
		(touch0state & S3E_POINTER_STATE_PRESSED)  ) {
		
			// check for every ship, but it can happen that multiple ships are being touched
			// we only want the nearest ship, because it is most likely the player wanted to control
			// the nearest ship
			float nearest_dist = INFINITY;
			int nearest = -1;

			CIwFVec2 touch_pos( (float) s3ePointerGetTouchX( 0 ), (float) s3ePointerGetTouchY( 0 ) );
			
			for( int i = 0; i< MAX_NUM_SHIPS; ++i ) {
				
				if( m_ships[i].TouchInArea( 0 ) ) {
					
					float distance = (m_ships[i].GetPos() - touch_pos).GetLengthSquared();
					
					if( distance < nearest_dist ) {
						nearest_dist = distance;
						nearest = i;
					}
				}
				
				// we do not need to check if we actually touched something
				// because the value for no touched ship us -1
				m_active_ship = nearest;
			}
		}
		
		for( int i = 0; i < MAX_NUM_SHIPS; ++i ) {
			m_docks[i].Update( dt );
		}
		
		for( int i = 0; i < MAX_NUM_SHIPS; ++i ) {
		
			// no need to check for ships which are not active
			if( m_ships[i].IsActive() == false ) continue;
			
			m_ships[i].Update( dt, m_active_ship == i );
			
			// check if we reached the maximum line nodes
			if( m_ships[i].ReachedMaxNodes() == true ) {
				m_active_ship = -1;
			}
			
			// check if the ship is outside the screen
			if( m_ships[i].OutsideScreen() == true && m_ships[i].WasAtDock() == true ) {
				m_current_level->ShipOutOfScreen( &m_ships[i] );
				m_ships[i].Disable();
			}
			
			CIwFVec2 normal;
			// check if the ship collided with the background
			if( m_background.CheckForCollision( m_ships[i], &normal) ) {
			
				// normal = CIwFVec2(0, 1);
				// check if we are already moving along the normal
				// away from the collision point
				if( fsign(m_ships[i].GetCurrentDir().y) == fsign(normal.y) ) {
				} else {
				
					CIwFVec2 dir = m_ships[i].GetCurrentDir();
					
					if( dir.GetLengthSquared() == 0 ) {
						// the current direction is 0,0
						// assume we would be moving along the normal
						// already, this makes sure we will move away
						// from the collision point in every case
						dir = normal;
					}
				
					m_ships[i].InvertMovement( dir, normal );
				}
			}
			
			// check for collision with all other ships
			for( int s = i + 1; s < MAX_NUM_SHIPS; ++s ) {
				if( m_ships[s].IsActive() == true && m_ships[s].OutsideScreen() == false ) {
				
					// make sure both centers are inside the screen
					if( m_ships[i].GetPos().x < 0 || m_ships[i].GetPos().x > SCRWIDTH ) continue;
					if( m_ships[s].GetPos().x < 0 || m_ships[s].GetPos().x > SCRWIDTH ) continue;
					
					if( m_ships[i].GetPos().y < 0 || m_ships[i].GetPos().y > SCRHEIGHT ) continue;
					if( m_ships[s].GetPos().y < 0 || m_ships[s].GetPos().y > SCRHEIGHT ) continue;
				
					if( OBB::CheckForOverlapOBB( m_ships[i].GetOBB(), m_ships[s].GetOBB() ) ) {
						// found a collision between ships
						// -> game over
						m_state = GameplayState::GAME_OVER;
						
						// make sure the current score is saved
						m_score_system->Save( );
						
						m_ships[i].Crashed();
						m_ships[s].Crashed();
						
						return -1;
					}
				}
			}
			
			// check for collision with the dock
			for( int d = 0; d < MAX_NUM_DOCKS; ++d ) {
				bool collision = OBB::CheckForOverlapOBB( m_ships[i].GetOBB(), m_docks[d].GetOBB() );
			
				if( collision ) {
					if( m_ships[i].IsDocking() == true ) {
						// the ship is still docking	
					} else if( m_ships[i].WasAtDock() == false && m_ships[i].GetTargetDock() == m_docks[d].GetDockType() ) {
						m_ships[i].StartDocking( m_docks[d] );
					}
				}
			}
		}
	} else if( m_state == GameplayState::PAUSED ) {
		if( level_update_stat != LevelUpdateReturnStatus::SHOW_OVERLAY )
		{
			int restart_update = m_pause_menu.Update( dt );
			
			if( restart_update == 1 ) {
				m_state = GameplayState::NORMAL;
			} else if(restart_update == 2) {
				return GameScreens::MAIN_GAMEPLAY_SCREEN;
			} else if( restart_update == 3 ) {
				return GameScreens::START_MENU_SCREEN;
			}
		} else {
			// reset the state again
			m_state = old_gameplay_state;
		}
		
	} else if( m_state == GameplayState::GAME_OVER ) {
		int restart_update = m_gameover_menu.Update( dt );
		
		if( restart_update == 1 ) {
			return GameScreens::MAIN_GAMEPLAY_SCREEN;
		} else if( restart_update == 2 ) {
			return GameScreens::START_MENU_SCREEN;
		}
	}
	
	return -1;
}

void MainGameplayScreen::Render()
{
	m_water.Draw();
	m_background.Draw();
	
	for( int i = 0; i < MAX_NUM_DOCKS; ++i ) {
		m_docks[i].Draw( );	
	}
	
	for( int i = 0; i < MAX_NUM_SHIPS; ++i ) {
		if( m_ships[i].IsActive() ) {
			m_ships[i].Draw( );
		}
	}
	
	m_gameplay_ui.Draw( m_score_system->GetCurrentScore(), m_score_system->GetCurrentHighscore() );
	
	if( m_state == GameplayState::PAUSED ) {
		m_pause_menu.Draw ();
	} else if( m_state == GameplayState::GAME_OVER ) {
		m_gameover_menu.Draw( );
	} else if( m_state == GameplayState::LEVEL_COMPLETE ) {
	
		int alpha = (int)(sin( m_level_complete_state ) * 255.0f);
	
		uint color = 0x00FFFFFF;
		
		// need to merge the alpha into it
		color = color | ( (uint) (alpha) << 24 );
		
		Iw2DSetColour( color );
		m_level_complete.Draw( CIwSVec2(100, 100) );
		Iw2DSetColour( 0xFFFFFFFF );
	}
	
	m_current_level->Draw( );
}
